#include "ReutersLoader.hpp"

#include <QFile>
#include <utility>
#include <QStringList>
#include <QXmlSimpleReader>
#include <QXmlInputSource>

#include <iostream>

#include "ReutersDataModel.hpp"
#include "ReutersDocument.hpp"
#include "ReutersDataView.hpp"
#include "ReutersXMLParser.hpp"


ReutersLoader::ReutersLoader(QObject *parent) :
    QObject(parent),
    dataModel_ (nullptr),
    dataView_ (nullptr)
{}


void ReutersLoader::loadClassification (QString file_name) {
    emit loadingClassification();

    filter_.clear();

    if (dataModel_ != nullptr) {
        delete dataModel_;
        delete dataView_;
    }
    dataModel_ = new ReutersDataModel ();
    dataView_ = new ReutersDataView ();

    QFile file(file_name);
    if (!file.open(QIODevice::ReadOnly)) {
        return;
    }

    // skip headers
    file.readLine();

    unsigned misclassified = 0;
    unsigned classified_ok = 0;

    QString id;
    QString doc_file;
    QString real_class;
    QString predicted_class;
    QString title;

    bool next_quote_opens = true;
    int step = -1;
    QString* atrs [] = {&id, &doc_file, &real_class, &predicted_class, &title};

    bool escaped = false;

    while (!file.atEnd()) {
        char c;
        file.getChar(&c);
        if (c == '\\') {
            escaped = true;
        } else if (c == '"' && !escaped) {
            if (next_quote_opens) {
                ++step;
            } else if (atrs[step] == &title) {
                ReutersDocument* d = dataModel_->document(doc_file);
                if (d == nullptr)
                    d = dataModel_->createDocument(doc_file, title);
                dataModel_->assignClass(doc_file, predicted_class);
                if (real_class == predicted_class)
                    ++classified_ok;
                else
                    ++misclassified;
                id.clear();
                doc_file.clear();
                real_class.clear();
                predicted_class.clear();
                title.clear();
                step = -1;
            }
            next_quote_opens = !next_quote_opens;
        } else if (c != '\n' && !next_quote_opens) {
            atrs[step]->append(c);
            if (escaped) escaped = false;
        }
    }

    emit loadedClassification(dataModel_);
    dataView_->setAllDocuments(dataModel_->allDocuments());
    emit loadedFilter(dataView_);
    emit clsAcc(100.0f * static_cast<float>(classified_ok) / static_cast<float>(misclassified + classified_ok));
}


void ReutersLoader::loadDocument (ReutersDocument* document) {
    emit loadingDocument();
    QFile file ("./pcorpus/" + document->file());
    if (!file.exists()) {
        file.setFileName("../pcorpus/" + document->file());
        if (!file.exists()) {
            std::cout << "Dziwne, nie moge znalezc pliku " << document->file().toStdString() << "..." << std::endl;
            emit loadedDocument(nullptr);
            return;
        }
    }

    QXmlInputSource xml_input (&file);
    QXmlSimpleReader xml_reader;
    ReutersXMLParser xml_parser (document);

    xml_reader.setContentHandler(&xml_parser);
    xml_reader.setErrorHandler(&xml_parser);

    if (xml_reader.parse(&xml_input)) {
        emit loadedDocument(document);
    } else {
        emit loadedDocument(nullptr);
    }
    file.close();
}


void ReutersLoader::loadFilter (QString oOoOoO) {
    QList<QString> filter = oOoOoO.split(";");

    QSet<QString> new_filter = QSet<QString>::fromList(filter);

    filter_.subtract(new_filter);
    if (!filter_.empty()) {
        // removed filters:
        dataView_->clear();
        for (QString f : filter) {
            if (!f.isEmpty())
                dataView_->add(dataModel_->docsInClass(f));
        }
    } else {
        // new filters:
        QSet<QString> new_filters = new_filter;
        new_filters.subtract(filter_);
        for (QString nf : new_filters) {
            if (!nf.isEmpty())
                dataView_->add(dataModel_->docsInClass(nf));
        }
    }

    filter_ = new_filter;
    dataView_->updateFinished();

    emit loadedFilter(dataView_);
}
